package cn.javabb.core.service.sys;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Validator;
import cn.javabb.common.exception.BusinessException;
import cn.javabb.common.utils.ListUtils;
import cn.javabb.core.base.BaseService;
import cn.javabb.core.constant.Constant;
import cn.javabb.core.entity.sys.UserAuthDO;
import cn.javabb.core.entity.sys.UserDO;
import cn.javabb.core.mapper.sys.UserAuthMapper;
import cn.javabb.core.model.LoginType;
import cn.javabb.core.model.StatusEnum;
import cn.javabb.core.plugin.oauth.RequestFactory;
import cn.javabb.core.shiro.UserToken;
import cn.javabb.core.util.EndecryptUtil;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.AuthRequest;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.List;

/**
 * @desc:
 * @author: javabb (javabob(a)163.com)
 * @create: 2019/11/11 11:05
 */
@Service
@Slf4j
public class UserAuthService extends BaseService<UserAuthDO> {

    @Autowired
    private UserAuthMapper userAuthMapper;

    @Autowired
    private UserService userService;

    public String getAuthType(String userName) {
        String authType = Constant.USER_AUTH_ACCOUNT;
        if (Validator.isEmail(userName)) {
            authType = Constant.USER_AUTH_EMAIL;
        } else if (Validator.isMobile(userName)) {
            authType = Constant.USER_AUTH_PHONE;
        }
        return authType;
    }

    /**
     * 具体登陆流程，调用自定义的realm实现第三方登陆
     * @param source
     * @param callback
     */
    public void authLogin(String source, AuthCallback callback,UserDO loginUser) {
        AuthRequest authRequest = RequestFactory.getInstance(source).getRequest();
        AuthResponse response = authRequest.login(callback);
        if(response.ok()){
            AuthUser authUser = (AuthUser) response.getData();
            if (null == loginUser) {
                UserDO newUser = this.userAuth(authUser);
                UserToken token = new UserToken(newUser.getUserName(),"", LoginType.OAUTH.getDesc());
                // 获取当前的Subject
                Subject currentUser = SecurityUtils.getSubject();
                try {
                    log.info("对用户[" + newUser.getUserName() + "]进行登录验证..验证开始");
                    currentUser.login(token);
                    log.info("对用户[" + newUser.getUserName() + "]进行登录验证..验证通过");
                }catch (Exception ex){
                    ex.printStackTrace();
                    log.error("第三方登陆失败==》authId = {} ,",authUser.getUuid());
                    throw new BusinessException("第三方登陆失败"+ex);
                }
            }else{
                this.userBindAuth(authUser, loginUser);
            }

        }
    }

    /**
     * 账号绑定，
     * @return
     */
    @Transactional
    public UserDO userAuth(AuthUser auth){
        UserAuthDO userAuth = this.queryOne(
                new UserAuthDO()
                .setAuthType(auth.getSource().toString())
                .setAuthId(auth.getUuid())
                .setState(StatusEnum.NORMAL.getCode())
                );
        if (null == userAuth) {
            // 授权第三方登陆
            // 先创建用户
            UserDO user = new UserDO()
                    .setNickName(auth.getNickname())
                    .setUserName(auth.getUsername())
                    .setUserGrade(0)
                    .setUserAddress(auth.getLocation())
                    .setUserImg(auth.getAvatar())
                    .setState(StatusEnum.NORMAL.getCode())
                    ;
            userService.insertSelective(user);

            userAuth = new UserAuthDO()
                    .setUserId(user.getId())
                    .setAuthId(auth.getUuid())
                    .setAuthType(auth.getSource().toString())
                    .setState(StatusEnum.NORMAL.getCode())
                    .setAccessToken(auth.getToken().getAccessToken())
                    .setAvatar(auth.getAvatar())
                    .setNickName(auth.getUsername())
                    ;
            this.insertSelective(userAuth);
            return user;
        }else{
            return userService.queryById(userAuth.getUserId());
        }
    }
    public boolean userBindAuth(AuthUser authUser,UserDO loginUser){

        UserAuthDO ua = this.queryOne(new UserAuthDO()
                .setUserId(loginUser.getId())
                .setAuthType(authUser.getSource().toString())
                .setState(StatusEnum.NORMAL.getCode()));

        if (null == ua) {
            UserAuthDO newUa = new UserAuthDO()
                    .setUserId(loginUser.getId())
                    .setAuthType(authUser.getSource().toString())
                    .setState(StatusEnum.NORMAL.getCode())
                    .setAuthId(authUser.getUuid())
                    .setAccessToken(authUser.getToken().getAccessToken())
                    .setAvatar(authUser.getAvatar());
            if (this.insertSelective(newUa) > 0) {
                return true;
            }
        }else{
            throw new BusinessException("当前三方账号已经授权过了");
        }
        return false;
    }

    public boolean revokeAuth(String authType, Integer userId) {
        UserAuthDO userAuth = new UserAuthDO().setAuthType(authType).setUserId(userId);
        return this.deleteByWhere(userAuth)>0?true:false;
    }
    /**
     *
     * @param authId
     * @return
     */
    public UserAuthDO getUserAuthByAuthId(String authId) {
        UserAuthDO userAuth = new UserAuthDO().setAuthId(authId).setState(StatusEnum.NORMAL.getCode());
        return this.queryOne(userAuth);
    }
    /**
     * 获取用户的授权账号
     * @param userId
     * @param authType
     * @return
     */
    public UserAuthDO getUserAuth(Integer userId,String authType) {
        UserAuthDO userAuth = new UserAuthDO().setUserId(userId).setAuthType(authType).setState(StatusEnum.NORMAL.getCode());
        return this.queryOne(userAuth);
    }

    /**
     * 根据用户ID获取所有授权账号
     * @param userId
     * @return
     */
    public List<UserAuthDO> listUserAuthByUserId(Integer userId) {
        UserAuthDO userAuth = new UserAuthDO().setUserId(userId);
        return this.queryListByWhere(userAuth);
    }

    /**
     * 重置用户密码
     * @param userId
     * @return
     */
    public boolean resetAuthPwd(Integer userId){
        UserAuthDO ua = this.queryOne(new UserAuthDO().setUserId(userId).setAuthType(Constant.USER_AUTH_ACCOUNT));
        if (ua == null) {
            return false;
        }
        ua.setAuthPwd(EndecryptUtil.encrytMd5(Constant.INIT_PWD));
        if (this.updateSelective(ua) > 0) {
            return true;
        }
        return false;
    }
    /*保存账号权限*/
    @Transactional(rollbackFor = Exception.class)
    public boolean save(UserAuthDO userAuthDO) {
        Assert.notNull(userAuthDO.getUserId(),"用户ID不能为空");
        UserDO user = userService.queryById(userAuthDO.getUserId());
        if(StringUtils.isEmpty(userAuthDO.getAuthId())){
            userAuthDO.setNickName(user.getNickName());
            userAuthDO.setAuthPwd(EndecryptUtil.encrytMd5(userAuthDO.getAuthPwd())); // 保存的时候保存密码
            userAuthDO.setState(StatusEnum.NORMAL.getCode());
            this.insertSelective(userAuthDO);
        }else{  //更新的时候不用更新密码，如果需要修改密码，用密码重置功能
            this.updateSelective(userAuthDO);
        }
        return true;
    }
    /*一种授权类型只能有一个账号绑定*/
    public boolean checkExist(UserAuthDO userAuth) {
        Assert.notNull(userAuth.getUserId(),"用户ID不能为空");
        int num = this.queryCountByWhere(
                new UserAuthDO()
                .setUserId(userAuth.getUserId())
                .setAuthType(userAuth.getAuthType())
                .setState(StatusEnum.NORMAL.getCode())
        );
        return num>0;
    }
    /*删除授权*/
    public boolean delete(String ids) {
        List<String> idList = ListUtils.stringToListString(ids);
        int num = 0;
        for (String id : idList) {
            num += this.deleteById(id);
        }
        return num>0;
    }
}
